<?php

/*
 * To change this template, choose Tools | Templates
 * and open the template in the editor.
 */

/**
 * Description of BMessager
 *
 * @author I062261
 */
class BMessenger extends BEntity {

    //put your code here

    public $operation = null;
    public $config = null;
    protected $sendContextList = array();
    protected $receiveContextList = array();
    protected $messageManager = null;

    //protected $addressManager = null;

    public function __construct($item = NULL) {
        if ($item instanceof BOperation)
            $this->operation = $item;
        else {
            $this->operation = Blyn::app()->getActiveOperation();
        }
        parent::__construct();
    }

    /**
     * use to add receiveContext into datastore
     * @param type $context
     */
    public function addReceiveContext($context) {
        
    }

    /**
     * use to add send context into data store
     * @param type $context
     */
    public function addSendContext($context) {
        
    }

    /**
     * 
     * @param type $message, messageName, define in config of defining message
     * @param type $receiver, contain address, receiver context and other receiver info
     * @param type $sender, contain sender address, sender context and other sender info
     * @param type $data, the mssage data to send, usually be array in key=value format
     * @return boolean
     */
    public function sendMessage($message = NULL, $receiver = NULL, $data = NULL, $sender = NULL) {

        $checkedReceiver = FALSE;
        $checkedSender = FALSE;
        $messageName = NULL;
        $messageContent = array();
        $oSender = NULL;
        $oReceiver = NULL;

        if (is_string($message)) {
            $messageName = $message;
        }

        if (is_array($message)) {
            if (isset($message[0]) && is_string($message[0])) {
                $messageName = $message[0];
                unset($message[0]);
            }
            if (isset($message['name'])) {
                $messageName = $message['name'];
                unset($message['name']);
            }
            $messageContent = $message;
        }

        if ($data != NULL)
            $messageContent = $data;

        $messengerContext = $this->operation->getConfigManager()->getConfig('messenger');
        $messageContext = NULL;
        if (is_array($messengerContext) && $messengerContext != NULL) {
            if (isset($messengerContext[$messageName])) {
                $messageContext = $messengerContext[$messageName];
            }
            if ($messageContext == NULL) {
                $messageContext[key($messengerContext)] = reset($messengerContext);
                //by default, use first send message config as send context
                //$sendContext = $sendMessageConfig[0];
            }
        }

        $addressObj = new BAddress();
        $addressObj->theApp = $this->operation->getApp();

        $messageClass = 'BMessage';

        $oMessageContext = BMessageContext::buildFromArray($messageContext);

        $oMessage = new $messageClass();

        //populate receiver and sender
        if (is_array($receiver)) {
            $oReceiver = BAddress::buildFromArray($receiver, $addressObj);
        } else if (is_string($receiver)) {
            $oReceiver = BAddress::buildFromString($receiver, $addressObj);
        } else {
            if (isset($messageContext['receiver'])) {
                $receiver = $messageContext['receiver'];
                $oReceiver = is_string($receiver) ? BAddress::buildFromString($receiver, $addressObj) : BAddress::buildFromArray($receiver, $addressObj);
            }
        }

        //check receiver
        if ($receiver == NULL)
            return FALSE;

        if (is_array($sender)) {
            $oSender = BAddress::buildFromArray($sender, $addressObj);
        } else if (is_string($sender)) {
            $oSender = BAddress::buildFromString($sender, $addressObj);
        } else {
            if ($sender == NULL) {
                $sender = array();
                $sender['operationId'] = Blyn::app()->getActiveOperation()->getId();
                $sender['userId'] = Blyn::app()->getCurrentUser()->getId();
                $oSender = BAddress::buildFromArray($sender, $addressObj);
            }
        }

        //check allow_sender
        if (isset($messageContext['allow_sender'])) {
            $allowSender = $messageContext['allow_sender'];
        } else {
            $allowSender = array();
            $allowSender['operationId'] = $this->operation->getId();
        }

        $oAllowSender = BAddress::buildFromArray($allowSender);

        //$checkAllowSender = $oSender->filterByContext($oAllowSender);
        $checkAllowSender = BAddress::checkAllowAddress($sender, $allowSender);

        if (!$checkAllowSender)
            return FALSE;

        //check allow_to
        if (isset($messageContext['allow_to'])) {
            $allowTo = $messageContext['allow_to'];
        } else {
            $allowTo = array();
            $allowTo['operationId'] = $this->operation->getId();
        }

        $oAllowTo = BAddress::buildFromArray($allowTo);

        $checkAllowTo = BAddress::checkAllowAddress($receiver, $allowTo);

        //$checkAllowTo = $oReceiver->filterByContext($oAllowTo);

        if (!$checkAllowTo)
            return FALSE;

        //check allow_to
        //populate receiver from messageContext
        $receiverContext = $oMessageContext->allow_to;

        if ($receiver == NULL) {
            $receiver = $receiverContext;
            $checkedReceiver = TRUE;
        }

        /*
         * below  code execute send message action,
         * first check whether to send message
         * then save address, then save message
         */
        if ($oSender instanceof BAddress && $oMessage instanceof BMessage && $oMessageContext instanceof BMessageContext) {
            //if ($sendContext->checkSendPermit($sender, $message)) {
            //before saving, filter address
            $oSender->filterByContext($allowSender);
            $senderAddressId = $oSender->save(array('addressName' => uniqid('s'), 'way' => 'sender'));
            $oReceiver->id = NULL;
            $oReceiver->filterByContext($allowTo);
            $receiverAddressId = $oReceiver->save(array('addressName' => uniqid('r'), 'way' => 'receiver'));

            $oMessage->name = $messageName;
            $oMessage->senderAddress = $senderAddressId;
            $oMessage->receiverAddress = $receiverAddressId;
            $oMessage->sendTime = time();

            $messageId = $oMessage->save($messageContent);

            $sendResult = FALSE;

            // if (isset($receiver['operationId'])) {
            $opList = $oReceiver->getOperationList();
            foreach ($opList as $opId) {

                $rOperation = new BOperation($opId);
                $sendResult = $rOperation->getMessenger()->checkMessageStatus($messageId);

                //test receive messages
                $messageView = $rOperation->getMessenger()->receiveMessages('processJoinSpace');
            }

            //test receive messages


            return $sendResult;

            /*
              if ($message->getId() > 0) {
              $mediaManager = $message->getMediaManager();
              //to support update, you should first find media
              //$media = $mediaManager->getMedia($message);
              //if (!$media instanceof BWMedia) {
              $media = new BMedia();
              $media->name = 'text';
              $media->content = $content;
              //}
              $mediaManager->addMedia($media);
              }
             * 
             */
            //}
        }
        return FALSE;
    }

    /**
     * 
     * @param type $receiver, contain receiver address, receiver context and other info
     * @param type $sender, contain sender address, sender context and other info
     * @param type $message
     * @param type $data
     * @return type
     */
    public function receiveMessages($message, $sender = NULL, $receiver = NULL, $data = NULL) {

        $messageName = NULL;

        if (is_string($message)) {
            $messageName = $message;
        }

        if (is_array($message)) {
            if (isset($message[0]) && is_string($message[0])) {
                $messageName = $message[0];
                unset($message[0]);
            }
            if (isset($message['name'])) {
                $messageName = $message['name'];
                unset($message['name']);
            }
            $messageContent = $message;
        }

        if ($messageName == NULL)
            return FALSE;

        $addressObj = new BAddress();
        $addressObj->theApp = $this->operation->getApp();

        $messengerContext = $this->operation->getConfigManager()->getConfig('messenger');
        $messageContext = NULL;
        if (is_array($messengerContext) && $messengerContext != NULL) {
            if (isset($messengerContext[$messageName])) {
                $messageContext = $messengerContext[$messageName];
            }
            if ($messageContext == NULL) {
                $messageContext[key($messengerContext)] = reset($messengerContext);
                //by default, use first send message config as send context
                //$sendContext = $sendMessageConfig[0];
            }
        }

        //populate receiver
        if (is_array($receiver)) {
            $oReceiver = BAddress::buildFromArray($receiver, $addressObj);
        } else if (is_string($receiver)) {
            $oReceiver = BAddress::buildFromString($receiver, $addressObj);
        } else {
            $receiver = array();
            $receiver['operationId'] = Blyn::app()->getActiveOperation()->getId();
            $receiver['userId'] = Blyn::app()->getCurrentUser()->getId();
            $oReceiver = BAddress::buildFromArray($receiver, $addressObj);
        }

        //check allow receiver
        if (isset($messageContext['allow_receiver'])) {
            $allowReceiver = $messageContext['allow_receiver'];
        } else {
            $allowReceiver['operationId'] = $this->operation->getId();
        }

        $oAllowReceiver = BAddress::buildFromArray($allowReceiver, $addressObj);

        //$checkAllowReceiver = $oReceiver->filterByContext($oReceiver);
        $checkAllowReceiver = BAddress::checkAllowAddress($receiver, $allowReceiver);

        //populate receiver
        if ($sender == NULL) {
            $sender = array();
            $sender['operationId'] = Blyn::app()->getActiveOperation()->getId();
            $sender['userId'] = Blyn::app()->getCurrentUser()->getId();
        }
        if (is_array($sender)) {
            $oSender = BAddress::buildFromArray($sender, $addressObj);
        } else if (is_string($sender)) {
            $oSender = BAddress::buildFromString($sender, $addressObj);
        }

        //check allow from
        $allowFrom = NULL;
        if (isset($messageContext['allow_from'])) {
            $allowFrom = $messageContext['allow_from'];
        }

        $checkAllowFrom = BAddress::checkAllowAddress($sender, $allowFrom);

        if (!$checkAllowReceiver)
            return FALSE;

        //check allow from
        //get messages from database
        $addressItemTable = new BMessageSheet('addressItem');

        $receiverCondition = $this->populateAllowAddress($receiver, $allowReceiver);

        //test
        $receiverCondition = array('operationId' => 30);
        $receiverCondition['way'] = 'receiver';
        $addressValueList = $addressItemTable->getValueList('addressId', array('itemData' => $receiverCondition), NULL, TRUE, FALSE);

        $messageData = array();
        $messageData['receiverAddressId'] = $addressValueList;
        if (isset($allowFrom['message'])) {
            $sendingMessage = $allowFrom['message'];
        }
        if (isset($sendingMessage))
            $messageData['name'] = $sendingMessage;

        $messageTable = new BMessageSheet('message');
        $messageView = $messageTable->getView(array('itemData' => $messageData));
        return $messageView;



        /*
          if ($receiver == NULL) {
          $receiver = BAddress::buildFromString('this');
          $currentUserId = Blyn::app()->getCurrentUser()->getId();

          $currentOperationId = Blyn::app()->getActiveOperation()->getId();
          } else {
          if (is_string($receiver)) {
          $receiver = BAddress::buildFromString($address);
          }
          if (is_array($receiver)) {
          $receiver = BAddress::buildFromArray($receiver);
          }
          }

          $addressTable = new BSheet('addressItem');

          //get messages for operation
          if ($receiver instanceof BAddress) {
          $operationList = $receiver->getOperationList();
          $messageList = $receiver->getMessageList();
          $userList = $receiver->getUserList();
          $appList = $receiver->getappList();
          $spaceList = $receiver->getspaceList();
          $roleList = $receiver->getroleList();

          foreach ($operationList as $opId) {
          foreach ($messageList as $msg) {
          $itemData = array();
          $itemData['operationId'] = $opId;
          $itemData['message'] = $msg;

          $addressView = $addressTable->getView(array('itemData' => $itemData));
          }
          }
          }
          //get message for user

          if ($receiver instanceof BAddress) {
          //if ($receiveContext->checkReceivePermit($receiver, $message)) {

          $connection = BMMessage::model()->getDbConnection();
          $sql = 'SELECT DISTINCT c._id FROM bln_address a INNER JOIN bln_address_item b' .
          ' ON a._id = b.address_id INNER JOIN bln_message c' .
          ' ON a._id = c.to_address WHERE b.user_id = :user_id' .
          ' AND c.status = 0' .
          ' UNION SELECT DISTINCT c._id FROM bln_address a INNER JOIN bln_address_item b' .
          ' ON a._id = b.address_id INNER JOIN bln_message c' .
          ' ON a._id = c.to_address WHERE b.role_id IN (' .
          ' SELECT x.role_id FROM bln_role_user x WHERE x.user_id = :user_id)' .
          ' AND c.status = 0' .
          ' UNION SELECT DISTINCT c._id FROM bln_address a INNER JOIN bln_address_item b' .
          ' ON a._id = b.address_id INNER JOIN bln_message c' .
          ' ON a._id = c.to_address WHERE b.operation_id = :operation_id' .
          ' AND c.status = 0';

          $command = $connection->createCommand($sql);
          $command->bindValue(":user_id", $currentUserId, PDO::PARAM_INT);
          $command->bindValue(":operation_id", $currentOperationId, PDO::PARAM_INT);
          $rows = $command->queryAll();

          $messages = array();
          foreach ($rows as $row) {
          $messageId = $row['_id'];
          $messages[$messageId] = new BMessage($messageId);
          }

          return $messages;
          //}
          }

          return array();
         * 
         */
    }

    public function receiveJoinSpaceMessages($receiver = NULL, $data = NULL, $space = NULL) {
        if ($receiver == NULL) {
            $receiver = BAddress::buildFromString('this');
            $currentUserId = Blyn::app()->getCurrentUser()->getId();

            $currentOperationId = Blyn::app()->getActiveOperation()->getId();
        }

        if ($receiver instanceof BAddress) {
            //if ($receiveContext->checkReceivePermit($receiver, $message)) {            
            $connection = BMMessage::model()->getDbConnection();
            if ($data == NULL)
                $sql = 'SELECT DISTINCT c._id FROM bln_address a INNER JOIN bln_address_item b' .
                        ' ON a._id = b.address_id INNER JOIN bln_message c' .
                        ' ON a._id = c.to_address WHERE b.role_id IN (' .
                        ' SELECT x.role_id FROM bln_role x WHERE x.user_id = :user_id)';
            else
                $sql = 'SELECT DISTINCT c._id, b.role_id FROM bln_address a INNER JOIN bln_address_item b' .
                        ' ON a._id = b.address_id INNER JOIN bln_message c' .
                        ' ON a._id = c.to_address WHERE b.role_id IN ' . $data .
                        ' AND c.status = 0';

            $command = $connection->createCommand($sql);
            $command->bindValue(":user_id", $currentUserId, PDO::PARAM_INT);
            $command->bindValue(":operation_id", $currentOperationId, PDO::PARAM_INT);
            $rows = $command->queryAll();

            $currentSpace = $space;
            $requestUserRoles = array();
            $messages = array();
            foreach ($rows as $row) {
                $messageId = $row['_id'];
                $message = new BMessage($messageId);

                $senderAddressId = $message->senderAddress;
                $senderAddress = new BAddress($senderAddressId);
                $senderAddressItemList = $senderAddress->getAddressItemList();
                if (count($senderAddressItemList) > 0) {
                    foreach ($senderAddressItemList as $item)
                        $senderUserId = $item->userId;
                    if ($senderUserId > 0) {
                        $senderUser = new BUser($senderUserId);
                        $senderUserName = $senderUser->userName;

                        $reqRoleId = $row['role_id'];
                        if ($reqRoleId > 0) {
                            $requestRole = new BRole($reqRoleId);
                            $requestUserRoles[$messageId] = array('roleId' => $reqRoleId, 'userId' => $senderUserId,
                                'spaceId' => $currentSpace->getId(), 'appId' => $requestRole->appId, 'applyingRole' => $requestRole->name);
                        }
                    }
                }
            }

            return $requestUserRoles;
            //}
        }

        return array();
    }

    /**
     * base on data to create message obj
     * @param type $data
     */
    public function createMessage($message, $messageName = NULL) {

        $messageObj = null;

        $app = $this->operation->getApp();
        $appClass = get_class($app);
        $operation = $this->operation;

        $messengerConfig = $this->getConfigManager();
        if ($messageName == NULL)
            $messageConfig = reset($messengerConfig);
        else {
            $messageConfig = $messengerConfig[$messageName];
        }

        if ($message instanceof BMessage) {
            $messageObj = $message;
        }

        if (is_array($message)) {
            if (isset($messageConfig['class'])) {
                $class = $messageConfig['class'];
                $messageObj = new $class();
            } else {
                $messageObj = new BMessage();
            }

            //sender specify receiver should under config receiver
            if (isset($message['receiver'])) {
                //$messageObj = BMessage::addReceiver($message['receiver'], $messageObj);
            }


            if (isset($messageConfig['receiver'])) {
                //$operation->getMessenger()->adjustAddressByConditions($messageObj);
            }
        }

        return $messageObj;
    }

    public function removeJoinSpaceMessage($messageId = NULL) {
        $this->removeMessageStuff($messageId);
    }

    public function removeMessageStuff($messageId = NULL) {
        if ($messageId > 0) {
            $message = new BMessage($messageId);

            $senderAddressId = $message->senderAddress;
            if ($senderAddressId > 0) {
                $senderAddress = new BAddress($senderAddressId);
                $senderAddressItemList = $senderAddress->getAddressItemList();
                //remove all addressItem 1st
                if (count($senderAddressItemList) > 0) {
                    foreach ($senderAddressItemList as $id => $item) {
                        $item->delete($id);
                    }
                }

                //remove address
                $senderAddress->delete($senderAddressId);
            }

            $receiverAddressId = $message->receiverAddress;
            if ($receiverAddressId > 0) {
                $receiverAddress = new BAddress($receiverAddressId);
                $receiverAddressItemList = $receiverAddress->getAddressItemList();
                //remove all addressItem 1st
                if (count($receiverAddressItemList) > 0) {
                    foreach ($receiverAddressItemList as $id => $item)
                        $item->delete($id);
                }

                //remove address
                $receiverAddress->delete($receiverAddressId);
            }

            //remove all media
            $message->getMediaManager()->removeAllMedias();

            //remove resourceMedia
            $attributes = array();
            $attributes['_id'] = $messageId;
            $resourceMedia = BMResourceMedia::model()->findByAttributes($attributes);
            if ($resourceMedia != NULL && $resourceMedia instanceof BMResourceMedia)
                $resourceMedia->delete();

            //remove message in the end
            $message->delete($messageId);
        }
    }

    public function getMessageManager($data = NULL) {
        if ($this->messageManager == NULL) {
            $this->messageManager = new BMessageManager($this->operation);
        }
        return $this->messageManager;
    }

    /**
     * this function can use to return result after sendMessage
     * @param type $messageId
     * @param type $data
     * @return boolean, return one of three status: approved, processing, rejected
     */
    protected function answerMessage($messageId, $data = NULL) {
        $app = $this->operation->getApp();

        if (method_exists($app, 'processMessage')) {
            return $app->processMessage($this->operation, $messageId);
        }

        return FALSE;
    }

    public function checkMessageStatus($messageId, $data = NULL) {

        if ($messageId > 0) {
            $messageTable = new BMessageSheet('message');
            $messageList = $messageTable->getListById($messageId);

            $messageStatusItem = $messageList->getItem('messageStatus');

            if ($messageStatusItem) {
                $messageStatus = $messageStatusItem->getValue();

                if ($messageStatus == 'approved' || $messageStatus == 'rejected') {
                    return $messageStatus;
                }
            }

            $result = $this->answerMessage($messageId, $data);

            if ($result == 'rejected' || $result == 'approved') {
                $messageList->addItem('messageStatus', $result);
            }

            return $result;
        }

        return FALSE;
    }

    /**
     * todo-list: should 
     * @param type $address
     * @param type $addressContext
     */
    public function populateAllowAddress($address, $addressContext) {

        if (is_array($address) && is_array($addressContext)) {

            $condition = $address;

            foreach ($addressContext as $key => $value) {
                if (isset($address[$key])) {
                    //if context set as all, -1 or *, means support all address
                    if ($value <= 0 || $value == 'all' || $value == '*') {
                        //return FALSE;
                        unset($condition[$key]);
                    }
                }
            }

            return $condition;
        }
    }

    /**
     * must meet all conditions in context, so address must first use id
     * if can, if can not ,can use name
     * for context, also need to populate to id list first, if can not, use name
     * 
     * @param type $address
     * @param type $addressContext
     * @return boolean
     */
    public function checkAllowAddress($address, $addressContext) {

        if (is_array($address) && is_array($addressContext)) {

            $condition = $address;

            foreach ($addressContext as $key => $value) {
                if (isset($address[$key])) {
                    //if address not equal context value, reject
                    if ((is_numeric($value) && $value > 0) || (is_string($value) && $value != NULL && $value != '')) {
                        //return FALSE;
                        if ($value != $address[$key])
                            return FALSE;
                    }else {
                        unset($address[$key]);
                        unset($addressContext[$key]);
                    }
                }
            }

            return TRUE;
        }
    }

    /**
     * because sender or receiver are always defined by id,
     * some try to change context into id 
     * @param type $addressContext
     * @return type
     */
    public function adjustAddressContext($addressContext) {

        $space = Blyn::app()->getActiveSpace();
        $app = Blyn::app()->getActiveApp();
        $operation = Blyn::app()->getActiveOperation();

        if (isset($addressContext['space'])) {
            $spaceContext = $addressContext['space'];
            if (is_string($spaceContext)) {
                $space = BSpace::find(array('name' => $spaceContext));
            }
            if (is_numeric($spaceContext))
                $space = new BSpace($spaceContext);
        }

        if ($space instanceof BSpace) {
            $addressContext['spaceId'] = $space->getId();
            if (isset($addressContext['space']))
                unset($addressContext['space']);
        }

        if (isset($addressContext['app'])) {
            $appContext = $addressContext['app'];
            if (is_string($appContext)) {
                $app = BApp::find(array('name' => $appContext));
            }
            if (is_numeric($appContext))
                $app = BApp::getAppById($appContext);
        }

        if ($app instanceof BApp) {
            $addressContext['appId'] = $app->getId();
            if (isset($addressContext['app']))
                unset($addressContext['app']);
        }

        if (isset($addressContext['operation'])) {
            if (is_string($operation)) {
                $operationName = $addressContext['operation'];
                $opData = explode(':', $operationName);

                if (count($opData) > 1) {
                    $appName = $opData[0];
                    $opName = $opData[1];
                    //$addressContext['appName'] = $appName;
                    //$addressContext['operationName'] = $opName;
                    if ($space instanceof BSpace && $app instanceof BApp) {
                        $app = $space->getAppByName($appName);
                        $operation = $app->getOperation(array('name' => $opName));
                        if ($operation instanceof BOperation) {
                            $addressContext['operationId'] = $operation->getId();
                            unset($addressContext['operation']);
                        }
                    }
                }
            }
            if (is_numeric($operation)) {
                $addressContext['operationId'] = $addressContext['operation'];
            }
        }

        if (isset($addressContext['role'])) {
            $roleContext = $addressContext['role'];
            if (is_numeric($roleContext)) {
                $addressContext['roleId'] = $roleContext;
                unset($addressContext['role']);
            }
            if (is_string($roleContext)) {
                if ($app instanceof BApp) {
                    $role = $app->getRole($roleContext);
                    if ($role instanceof BRole) {
                        $addressContext['roleId'] = $role->getId();
                        unset($addressContext['role']);
                    }
                }
            }
        }



        if (isset($addressContext['user'])) {
            if (isset($addressContext['user'])) {
                $userContext = $addressContext['user'];
                if (is_numeric($userContext)) {
                    $addressContext['userId'] = $userContext;
                    unset($addressContext['user']);
                }
                if (is_string($userContext)) {
                    if ($app instanceof BApp) {
                        $user = BUser::find($userContext);
                        if ($user instanceof BUser) {
                            $addressContext['userId'] = $user->getId();
                            unset($addressContext['user']);
                        }
                    }
                }
            }
        }

        return $addressContext;
    }

}

?>
